package mh;

import static org.lwjgl.opengl.GL11.GL_BLEND;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST;
import static org.lwjgl.opengl.GL11.GL_MODELVIEW;
import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.GL_PROJECTION;
import static org.lwjgl.opengl.GL11.GL_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL11.glBlendFunc;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glDisable;
import static org.lwjgl.opengl.GL11.glEnable;
import static org.lwjgl.opengl.GL11.glLoadIdentity;
import static org.lwjgl.opengl.GL11.glMatrixMode;
import static org.lwjgl.opengl.GL11.glOrtho;

import java.awt.event.KeyEvent;

import mh.map.TextureLoader;

import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;

import de.matthiasmann.twl.GUI;

/**
 * An implementation of GameWindow that will use OPENGL (JOGL) to render the scene. Its also responsible for monitoring
 * the keyboard using AWT.
 * 
 * @author Kevin Glass
 * @author Brian Matzon
 */
public class GameWindow {

	/** The callback which should be notified of window events */
	private GameWindowCallback		callback;

	/** True if the game is currently "running", i.e. the game loop is looping */
	private boolean					gameRunning	= true;

	/** The width of the game display area */
	private int						width;

	/** The height of the game display area */
	private int						height;

	/** The loader responsible for converting images into OpenGL textures */
	private static TextureLoader	textureLoader;

	/** Title of window, we get it before our window is ready, so store it till needed */
	private String					title;
	/** The GUI used by TWL to handle menu and swing component in OpenGL ambient */
	private GUI						gui;

	/**
	 * Create a new game window that will use OpenGL to render our game.
	 */
	public GameWindow() {
	}

	/**
	 * Retrieve access to the texture loader that converts images into OpenGL textures. Note, this has been made package
	 * level since only other parts of the JOGL implementations need to access it.
	 * 
	 * @return The texture loader that can be used to load images into OpenGL textures.
	 */
	public static TextureLoader getTextureLoader() {
		return textureLoader;
	}

	/**
	 * Set the title of this window.
	 * 
	 * @param title
	 *            The title to set on this window
	 */
	public void setTitle(String title) {
		this.title = title;
		if (Display.isCreated()) {
			Display.setTitle(title);
		}
	}

	/**
	 * Set the resolution of the game display area.
	 * 
	 * @param x
	 *            The width of the game display area
	 * @param y
	 *            The height of the game display area
	 */
	public void setResolution(int x, int y) {
		this.width = x;
		this.height = y;
	}

	/**
	 * Sets the display mode for screen mode
	 */
	private void setDisplayMode() {
		try {
			Display.setFullscreen(false);
			Display.setDisplayMode(new DisplayMode(this.width, this.height));
		} catch (LWJGLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Start the rendering process. This method will cause the display to redraw as fast as possible.
	 */
	public void startRendering() {
		try {
			this.setDisplayMode();
			Display.create();

			Mouse.setGrabbed(false);

			// enable textures since we're going to use these for our sprites
			glEnable(GL_TEXTURE_2D);

			// disable the OpenGL depth test since we're rendering 2D graphics
			glDisable(GL_DEPTH_TEST);
			// Overlap of images with trasparency
			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();

			glOrtho(0, this.width, this.height, 0, -1, 1);

			GameWindow.textureLoader = new TextureLoader();

			if (this.callback != null) {
				this.callback.initialise();
			}
			this.setTitle(this.title);

			//			LWJGLRenderer renderer = new LWJGLRenderer();
			//			GameUIDemo gameUI = new GameUIDemo();
			//			gui = new GUI(gameUI, renderer);
			//
			//			ThemeManager theme;
			//			try {
			//				theme = ThemeManager.createThemeManager(GameUIDemo.class.getResource("gameui.xml"), renderer);
			//				gui.applyTheme(theme);
			//			} catch (IOException e) {
			//				// TODO Auto-generated catch block
			//				e.printStackTrace();
			//			}

		} catch (LWJGLException le) {
			this.callback.windowClosed();
		}

		this.gameLoop();
	}

	/**
	 * Register a callback that will be notified of game window events.
	 * 
	 * @param callback
	 *            The callback that should be notified of game window events.
	 */
	public void setGameWindowCallback(GameWindowCallback callback) {
		this.callback = callback;
	}

	/**
	 * Check if a particular key is current pressed.
	 * 
	 * @param keyCode
	 *            The code associated with the key to check
	 * @return True if the specified key is pressed
	 */
	public boolean isKeyPressed(int keyCode) {
		// apparently, someone at decided not to use standard
		// keycode, so we have to map them over:
		switch (keyCode) {
			case KeyEvent.VK_SPACE:
				keyCode = Keyboard.KEY_SPACE;
				break;
			case KeyEvent.VK_LEFT:
				keyCode = Keyboard.KEY_LEFT;
				break;
			case KeyEvent.VK_RIGHT:
				keyCode = Keyboard.KEY_RIGHT;
				break;
			case KeyEvent.VK_UP:
				keyCode = Keyboard.KEY_UP;
				break;
			case KeyEvent.VK_DOWN:
				keyCode = Keyboard.KEY_DOWN;
				break;
			case KeyEvent.VK_A:
				keyCode = Keyboard.KEY_A;
				break;
			case KeyEvent.VK_D:
				keyCode = Keyboard.KEY_D;
				break;
			case KeyEvent.VK_W:
				keyCode = Keyboard.KEY_W;
				break;
			case KeyEvent.VK_S:
				keyCode = Keyboard.KEY_S;
				break;
		}

		return Keyboard.isKeyDown(keyCode);
	}

	/**
	 * Run the main game loop. This method keeps rendering the scene and requesting that the callback update its screen.
	 */
	private void gameLoop() {
		while (this.gameRunning) {
			// clear screen
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();

			// let subsystem paint
			if (this.callback != null) {
				this.callback.frameRendering();
			}

			// update window contents
			//			gui.update();
			Display.update();

			if (Display.isCloseRequested()) {
				// Unfortunately playing with the mouse it's possible that the player press the close button. In that
				// case the player will be asked for confirmation
				this.gameRunning = false;
				//				gui.destroy();
				Display.destroy();
				this.callback.windowClosed();
			}
		}
	}
}